package com.shelpe.services.simbasync.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import com.shelpe.services.simbasync.dto.CampaignsDto;
import com.shelpe.services.simbasync.dto.RptCampaignDto;
import com.shelpe.services.simbasync.exception.RequestParamException;
import com.shelpe.services.simbasync.model.Campaigns;
import com.shelpe.services.simbasync.model.RptCampaign;
import com.shelpe.services.simbasync.service.AuthService;
import com.shelpe.services.simbasync.service.CampaignsService;
import com.shelpe.services.simbasync.service.SyncRecordService;
import com.shelpe.services.simbasync.service.UserService;
import com.shelpe.services.simbasync.service.simbaproxy.CampaignsProxy;
import com.shelpe.services.simbasync.util.Util;


@RestController
@RequestMapping(value="/simbasync")
public class CampaignsController {
	
	private static final Logger logger = LoggerFactory.getLogger(CampaignsController.class);
	
	private static final Map<String, Date> requestPool = new HashMap<String, Date>();
	
	@Autowired
	private CampaignsProxy proxy;
	
	@Autowired
	private CampaignsService service;
	
	@Autowired
	private UserService user;
	
	@Autowired
	private AuthService auth;
	
	@Autowired
	private SyncRecordService syncService;
	
	private final ExecutorService executor = Executors.newFixedThreadPool(4);
	
	@RequestMapping("/tenants/{user}")
	@ResponseBody
	public CampaignsDto syncCampaignsByUser(@PathVariable("user") String user){
	
		boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		
		return isNumber ? syncCampaigns(null, Long.valueOf(user), false) : syncCampaigns(user, null, false);
	}
	
	@RequestMapping("/tenants/{user}/@SingleThread")
	@ResponseBody
	public CampaignsDto syncCampaignsByUserSingleThread(@PathVariable("user") String user){
		
		boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		
		return isNumber ? syncCampaigns(null, Long.valueOf(user), true) : syncCampaigns(user, null, true);
	}
	
	
	@RequestMapping("/tenants/{user}/campaigns/{campaign_id}")
	@ResponseBody
	public CampaignsDto syncCampaignByUser(@PathVariable("user") String user, @PathVariable("campaign_id") Long campaignId){
		
		boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		
		return isNumber ? syncCampaign(null, Long.valueOf(user), campaignId) : syncCampaign(user, null, campaignId);
	}
	
	@RequestMapping("/tenants/{user}/campaigns/{campaign_id}/@campaignasyn")
	@ResponseBody
	public DeferredResult<CampaignsDto> syncCampaignByUserAsyn(@PathVariable("user") final String user, @PathVariable("campaign_id") final Long campaignId){
		
		final DeferredResult<CampaignsDto> result = new DeferredResult<CampaignsDto>();
		final boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		CompletionService<CampaignsDto> comletionService = new ExecutorCompletionService<CampaignsDto>(executor); 
		
		comletionService.submit(new Callable<CampaignsDto>(){
                                                                                                                                                                                                    
			@Override
			public CampaignsDto call() throws Exception {
				// TODO Auto-generated method stub
				CampaignsDto dto = null;
				if(isNumber){
					dto = syncCampaign(null, Long.valueOf(user), campaignId);
				}else{
					dto = syncCampaign(user, null, campaignId);
				}
				
				result.setResult(dto);
				
				return dto;
			}});
		
		return result;
	}
	
	
	@RequestMapping("/tenants/{user}/@rptcampaign")
	@ResponseBody
	public RptCampaignDto syncRptCampaignsByUser(@PathVariable("user") String user,
			@RequestParam(value = "start_date", required = false) String startDate, @RequestParam(value = "end_date", required = false) String endDate,
			@RequestParam(value = "record", required = false) String record){
		
		boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		
		return isNumber ? syncRptCampaigns(null, Long.valueOf(user), startDate, endDate, record) : syncRptCampaigns(user, null, startDate, endDate, record);
	}
	
	@RequestMapping("/tenants/{user}/@rptcampaignasyn")
	@ResponseBody
	public DeferredResult<RptCampaignDto> syncRptCampaignByUserAsyn(@PathVariable("user") final String user, 
			@RequestParam(value = "start_date", required = false) final String startDate, @RequestParam(value = "end_date", required = false) final String endDate,
			@RequestParam(value = "record", required = false)final String record){
		
		final DeferredResult<RptCampaignDto> result = new DeferredResult<RptCampaignDto>();
		final boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		CompletionService<RptCampaignDto> comletionService = new ExecutorCompletionService<RptCampaignDto>(executor); 
		
		comletionService.submit(new Callable<RptCampaignDto>(){
                                                                                                                                                                                                    
			@Override
			public RptCampaignDto call() throws Exception {
				// TODO Auto-generated method stub
				RptCampaignDto dto = null;
				if(isNumber){
					dto = syncRptCampaigns(null, Long.valueOf(user), startDate, endDate, record);
				}else{
					dto = syncRptCampaigns(user, null, startDate, endDate, record);
				}
				
				result.setResult(dto);
				
				return dto;
			}});
		
		return result;
	}
	
	/*-----------------------------------------------------*/
	
	private CampaignsDto syncCampaigns(String nick, Long userId, boolean singleThread){
		
		if(userId == null){
			userId = user.getUserIdByNick(nick);
		}
		
		if(nick == null){
			nick = user.getNickByUserId(userId);
		}
		
		if(nick == null || userId == null){
			logger.warn("nick or userId is null, do nothing");
			throw new RequestParamException();
		}
		logger.info(">>>syncCampaigns nick {}, userId {}", nick, userId);
		CampaignsDto dto = new CampaignsDto();
		dto.setNick(nick);
		dto.setUserId(userId);
		dto.setProcess("syncCampaigns");
		Date beginProcessTime = new Date();
		
		String userIdStr = userId.toString();
		
		if(requestPool.containsKey(userIdStr)){
		
			Date lastProcessDate = requestPool.get(userIdStr);
			if(beginProcessTime.getTime() - lastProcessDate.getTime() > Util.MilliSecondsOfMin * 2){
				requestPool.remove(userIdStr);
				logger.info(">>>syncCampaigns nick {}, userId {} process out time, restart new sync task", nick, userId);
			}else{
				dto.setMessage("On processing");
				logger.info(">>>syncCampaigns nick {}, userId {} request too freq", nick, userId);
				return dto;
			}
		}

		requestPool.put(userIdStr, beginProcessTime);
		
		String response = null;
		try{
			response = proxy.getCampaignsByNick(nick);
		}catch(Exception e){
			logger.warn("Maybe something is wrong or can not find token, userId {}", userId);
		}
		
		if(response != null && response.contains("isv.invalid-permission")){//没有权限
			dto.setMessage("invalid-permission");
			requestPool.remove(userIdStr);
			return dto;
		}else if(response != null && response.contains("invalid-sessionkey")){
			dto.setMessage("invalid-sessionkey");
			logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
			requestPool.remove(userIdStr);
			return dto;
		}else if(response != null && response.contains("无法根据nick获取直通车帐号信息")){
			dto.setMessage("无法根据nick获取直通车帐号信息");
			logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
			requestPool.remove(userIdStr);
			return dto;
		}else if(response == null){
			dto.setMessage("get no information");
			requestPool.remove(userIdStr);
			return dto;
		}
		
		List<Campaigns> list = null;
		try{
			list = service.parseJsonToCampaigns(response);
			service.updateCampaignsData(userId, list, singleThread);
		}catch(Exception e){
			requestPool.remove(userIdStr);
			throw e; 
		}
	 
		List<Campaigns> oldListTemp = service.getCampaignsByUserId(userId);
		List<Campaigns> oldList = new ArrayList<Campaigns>();
		
		if(oldListTemp != null){
			oldList.addAll(oldListTemp);
		}

		Map<String, List<Campaigns> > splitCampaigns = service.splitCampaigns(list, oldList);
		
		dto.setAddNum(service.addCampaignsList(splitCampaigns.get("add")));
		dto.setUpdateNum(service.updateCampaignsList(splitCampaigns.get("update")));
		dto.setDeleteNum(service.deleteCampaignsList(splitCampaigns.get("delete")));
	
		Date endProcessTime = new Date();
		long durTime = endProcessTime.getTime() - beginProcessTime.getTime();
		dto.setDurTime(durTime);
		
		requestPool.remove(userIdStr);
		
		return dto;
	}
	

	private CampaignsDto syncCampaign(String nick, Long userId, Long campaignId){
	
		if(userId == null){
			userId = user.getUserIdByNick(nick);
		}
		
		if(nick == null){
			nick = user.getNickByUserId(userId);
		}
		
		if(nick == null || userId == null){
			logger.warn("nick or userId is null, do nothing");
			throw new RequestParamException();
		}
		logger.info(">>>syncCampaign nick {}, userId {}, campaignId {}", nick, userId, campaignId);
		CampaignsDto dto = new CampaignsDto();
		dto.setNick(nick);
		dto.setUserId(userId);
		dto.setProcess("syncCampaign");
		Date beginProcessTime = new Date();
		
		String requestParam = Util.createKey(null, userId.toString(), campaignId.toString(), null, null);
		if(requestPool.containsKey(requestParam)){
			
			Date lastProcessDate = requestPool.get(requestParam);
			if(beginProcessTime.getTime() - lastProcessDate.getTime() > Util.MilliSecondsOfMin){
				requestPool.remove(requestParam);
				logger.info(">>>syncCampaign nick {}, userId {}, campaignId {}, process out time, restart new sync task", nick, userId, campaignId);
			}else{
				dto.setMessage("On processing");
				logger.info(">>>syncCampaign nick {}, userId {}, campaignId {}, request too freq", nick, userId, campaignId);
				return dto;
			}
		}

		requestPool.put(requestParam, beginProcessTime);	
		
		String response = null;
		try{
			response = proxy.getCampaignsByNick(nick);
		}catch(Exception e){
			logger.warn("Maybe something is wrong or can not find token, userId {}", userId);
		}
		
		if(response != null && response.contains("isv.invalid-permission")){//没有权限
			dto.setMessage("invalid-permission");
			requestPool.remove(requestParam);
			return dto;
		}else if(response != null && response.contains("invalid-sessionkey")){
			dto.setMessage("invalid-sessionkey");
			logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
			requestPool.remove(requestParam);
			return dto;
		}else if(response == null){
			dto.setMessage("get no information");
			requestPool.remove(requestParam);
			return dto;
		}
		
		List<Campaigns> newList = new ArrayList<Campaigns>();
		try{
			List<Campaigns> list = service.parseJsonToCampaigns(response);
	
			for(Campaigns camp : list){
				if(camp.getCampaignId().equals(campaignId)){
					newList.add(camp);
					break;
				}
			}
			
			service.updateCampaignsData(userId, newList, true);
		}catch(Exception e){
			requestPool.remove(requestParam);
			throw e;
		}
	 
		Campaigns oldCampaign = service.getCampaignById(campaignId);
		
		if(oldCampaign == null){
			dto.setAddNum(service.addCampaignsList(newList));
		}else if(newList.size() == 0){
			dto.setDeleteNum(service.deleteCampaigns(oldCampaign));
		}else{
			dto.setUpdateNum(service.updateCampaignsList(newList));
		}
		
		Date endProcessTime = new Date();
		long durTime = endProcessTime.getTime() - beginProcessTime.getTime();
		dto.setDurTime(durTime);
		
		requestPool.remove(requestParam);
		
		return dto;
	}
	
	private RptCampaignDto syncRptCampaigns(String nick, Long userId, String startDate, String endDate, String record){
		
		if(userId == null){
			userId = user.getUserIdByNick(nick);
		}
		
		if(nick == null){
			nick = user.getNickByUserId(userId);
		}
		
		if(nick == null || userId == null){
			logger.warn("nick or userId is null, do nothing");
			throw new RequestParamException();
		}
		
		logger.info(">>>syncRptCampaigns nick {}, userId {}", nick, userId);
		RptCampaignDto dto = new RptCampaignDto();
		dto.setNick(nick);
		dto.setUserId(userId);
		dto.setProcess("syncRptCampaigns");
		Date beginProcessTime = new Date();
		
		String requestParam = Util.createKey("rpt", userId.toString(), null, null, endDate);
		if(requestPool.containsKey(requestParam)){
			
			Date lastProcessDate = requestPool.get(requestParam);
			if(beginProcessTime.getTime() - lastProcessDate.getTime() > Util.MilliSecondsOfMin){
				requestPool.remove(requestParam);
				logger.info(">>>syncRptCampaigns nick {}, userId {}, process out time, restart new sync task", nick, userId);
			}else{
				dto.setMessage("On processing");
				logger.info(">>>syncRptCampaigns nick {}, userId {}, request too freq", nick, userId);
				return dto;
			}
		}

		requestPool.put(requestParam, beginProcessTime);	
		
        String subwayToken = auth.getSubwayTokenByUserId(userId);
		
		if(subwayToken == null){
			logger.warn("can not find subwaytoken, nick is {}", nick);
			dto.setMessage("can not find subwaytoken");
			requestPool.remove(requestParam);
			return dto;
		}
		
		List<Campaigns> camList = service.getCampaignsByUserId(userId);
		if(camList == null){
			logger.warn("can not find any Campaigns, nick is {}", nick);
			dto.setMessage("can not find any Campaigns");
			requestPool.remove(requestParam);
			return dto;
		}
		
		String source = "1,2,4,5";
		String searchType = "SEARCH,CAT,NOSEARCH";
		int pageSize = 5000;
		
		List<RptCampaign> listRes = new ArrayList<RptCampaign>();
		Map<String, List<Long>> dateRange = new HashMap<String, List<Long>>();
			
		for(Campaigns cam : camList){
			
			long campaignId = cam.getCampaignId();
			Map<String, String> map = parseDateRange(userId, campaignId, startDate, endDate, dateRange);
			logger.info(">>>campaignId {}, startTime {}, endTime {}, pageSize {}", campaignId, map.get("startTime"), map.get("endTime"), map.get("pageSize"));
			
			if(map.get("startTime").compareTo(map.get("endTime")) < 1){//需要同步
				
				pageSize = NumberUtils.toInt(map.get("pageSize"), 5000);
				int pageNo = 1;
				List<RptCampaign> listTotal = new ArrayList<RptCampaign>();
				List<RptCampaign> subList = null;
				String response = null;
				try{
					
					do{
						response = proxy.getRptCampaignBase(subwayToken, nick, campaignId, source, searchType, map.get("startTime"), map.get("endTime"), pageSize, pageNo);
					
						if(response != null && response.contains("isv.invalid-permission")){//没有权限
							dto.setMessage("invalid-permission");
							requestPool.remove(requestParam);
							return dto;
						}else if(response != null && response.contains("invalid-sessionkey")){
							dto.setMessage("invalid-sessionkey");
							logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
							requestPool.remove(requestParam);
							return dto;
						}else if(response == null){
							break;
						}
						
						subList = service.parseJsonToRptCampaignsBase(response);
						if(subList != null && subList.size() > 0){
							listTotal.addAll(subList);
						}
						pageNo++;
					}while(subList != null && subList.size() == pageSize && pageSize > 0);
				
					pageNo = 1;
					int restSize = 0; 
					//int totalEffect = 0;
					
					if(listTotal != null && listTotal.size() > 0 && pageSize > 0){
						int lastDayRecordTotal = 0;
						int lastDayNoDataNum = 0;
						do{
							response = proxy.getRptCampaignEffect(subwayToken, nick, campaignId, source, searchType, map.get("startTime"), map.get("endTime"), pageSize, pageNo);
							Map<String, Integer> lastDayRecord = new HashMap<String, Integer>();
							restSize = service.combineRptCampaignEffect(response, lastDayRecord, listTotal);
							lastDayRecordTotal += lastDayRecord.get("total");
							lastDayNoDataNum += lastDayRecord.get("lost");
							//totalEffect+=restSize;
							pageNo++;
						}while(restSize == pageSize);
						
						if(record != null && record.equalsIgnoreCase("yes") 
								&& lastDayRecordTotal > 0 && lastDayNoDataNum == lastDayRecordTotal){//有基础数据，没有效果数据
							
							Date lastDate = Util.getLastDayYYYYMMDD();
							long lastCustRpt = user.countRptCustomerByUserIdDate(userId, lastDate);
							if(lastCustRpt == 0){
								logger.warn("syncRptCampaign, base data num {}, effect data num is zero, campaignId is {}", listTotal.size(), campaignId);
								syncService.updateSyncRecordByProcess("syncRptCampaign", 1);
								
								continue;//避免不同推广计划相互影响
							}	
						}
					}
					
				}catch(Exception e){
					requestPool.remove(requestParam);
					throw e;
				}
				
				listRes.addAll(listTotal);  //一个计划的数据信息
			}
		}
		
		service.updateRptCampaignsData(userId, listRes);
		
		if(startDate !=null && endDate != null){
			deleteRptDataByDateRange(dateRange);
		}
		
		dto.setAddNum(service.insertRptCampaignsBatch(listRes));
		
		Date endProcessTime = new Date();
		long durTime = endProcessTime.getTime() - beginProcessTime.getTime();
		dto.setDurTime(durTime);
		
		requestPool.remove(requestParam);
		
		return dto;
	}
	

	/************辅助函数******************/
	
	private Map<String, String> parseDateRange(Long userId, Long campaignId, String startDate, String endDate, Map<String, List<Long>> dateRange){
		
		Integer pageSize = 5000;
	
		Map<String, String> map = null;
		if(startDate == null || endDate == null){
			Date dateOld = service.getLastTimeFromRptByCampaign(userId, campaignId); 
			map = Util.getDateRange(dateOld);
			startDate = map.get("startTime");
			endDate = map.get("endTime");
			pageSize = NumberUtils.toInt(map.get("pageSize"), 5000);
		}
	
		if(startDate.compareTo(endDate) < 1){
			StringBuilder sb = new StringBuilder();
			sb.append(startDate);
			sb.append(",");
			sb.append(endDate);
			String range = sb.toString();
			if(dateRange.containsKey(range)){
				dateRange.get(range).add(campaignId);
			}else{
				List<Long> idsList = new ArrayList<Long>();
				idsList.add(campaignId);
				dateRange.put(range, idsList);
			}
		}
		
		if(map == null){
			map = new HashMap<String, String>();
			map.put("startTime", startDate);
			map.put("endTime", endDate);
			map.put("pageSize", pageSize.toString());
		}
		
		return map;
	}
	
	private void deleteRptDataByDateRange(Map<String, List<Long>> dateRange){//推广计划id
		
		for (Map.Entry<String, List<Long>> entry : dateRange.entrySet()){
			
			String key = entry.getKey();
			String[] range = key.split(",");
			Date startDate = new Date();
			Date endDate = startDate;
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			try {
				startDate = sdf.parse(range[0]);
				endDate = sdf.parse(range[1]);
			} catch (ParseException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		
			List<Long> campaignIds = entry.getValue();
			service.deleteRptCampaignsByDateRangeCamIds(campaignIds, startDate, endDate);
		}
	}
}